/*******************************************************************************
 * Copyright 2020 huanggefan.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package handler

import (
	"encoding/json"
	"fmt"
	"gitee.com/WisdomClassroom/core"
	"gitee.com/WisdomClassroom/resource/action"
	"gitee.com/WisdomClassroom/resource/global"
	"gitee.com/WisdomClassroom/resource/tools"
	"net/http"
	"time"
)

func publicPUTHandleFunc(resp http.ResponseWriter, req *http.Request) (bool, []byte, *action.FileInfo) {
	response := UploadResponse{}

	token, err := tools.GetTokenInfoFromCookies(req)
	if err != nil {
		response.StatusCode = ResponseCodeErrorNotAuth
		d, _ := json.Marshal(response)
		_, _ = resp.Write(d)
		resp.Header().Set("Content-Type", "application/json")
		return false, []byte{}, nil
	}

	if token.Type != core.UserRoleTypeCodeRoot {
		response.StatusCode = ResponseCodeErrorNotAuth
		d, _ := json.Marshal(response)
		_, _ = resp.Write(d)
		resp.Header().Set("Content-Type", "application/json")
		return false, []byte{}, nil
	}

	if !tools.ParseForm(resp, req) {
		response.StatusCode = ResponseCodeErrorFileTooBig
		d, _ := json.Marshal(response)
		_, _ = resp.Write(d)
		resp.Header().Set("Content-Type", "application/json")
		return false, []byte{}, nil
	}

	name, data, err := tools.GetFileOfForm(req)
	if err != nil {
		response.StatusCode = ResponseCodeErrorInvalidFile
		d, _ := json.Marshal(response)
		_, _ = resp.Write(d)
		resp.Header().Set("Content-Type", "application/json")
		return false, []byte{}, nil
	}

	info := &action.FileInfo{
		FileUUID:    core.GenUUID(),
		FileName:    name,
		PutTime:     time.Now().Unix(),
		PutUserUUID: token.UUID,
	}
	return true, data, info
}

func publicPUTSaveFunc(resp http.ResponseWriter, req *http.Request, data []byte, info *action.FileInfo) {
	response := UploadResponse{}

	chapterUUID, suc := action.CheckChapter(req)
	if !suc {
		response.StatusCode = ResponseCodeErrorChapterNotExist
		d, _ := json.Marshal(response)
		_, _ = resp.Write(d)
		resp.Header().Set("Content-Type", "application/json")
		return
	}
	info.ChapterUUID = chapterUUID

	err := action.SaveFile(data, info)
	if err != nil {
		response.StatusCode = ResponseCodeErrorSaveFailed
		d, _ := json.Marshal(response)
		_, _ = resp.Write(d)
		resp.Header().Set("Content-Type", "application/json")
		return
	}

	response.StatusCode = ResponseCodeSuc
	response.Info = FileInfoResponse{
		FileUUID:    info.FileUUID,
		FileType:    global.FileTypeCodeMap[info.FileType],
		FileName:    info.FileName,
		PutTime:     info.PutTime,
		PutUserUUID: info.PutUserUUID,
	}
	d, _ := json.Marshal(response)
	_, _ = resp.Write(d)
	resp.Header().Set("Content-Type", "application/json")
}

func GenPUTHandleFunc(fileType int) http.HandlerFunc {
	f := func(resp http.ResponseWriter, req *http.Request) {
		if req.Method != http.MethodPost {
			resp.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		suc, data, info := publicPUTHandleFunc(resp, req)
		if !suc {
			return
		}

		info.FileType = fileType
		publicPUTSaveFunc(resp, req, data, info)
	}

	return f
}

func HandleFuncGetResource(resp http.ResponseWriter, req *http.Request) () {
	if req.Method != http.MethodGet {
		resp.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	queryFileUUID, ok := tools.GetFileUUIDFromURL(req)
	if !ok {
		return
	}

	_, err := tools.GetTokenInfoFromCookies(req)
	if err != nil {
		return
	}

	fileData, fileInfo, err := action.GetFile(queryFileUUID)
	if err != nil {
		return
	}

	_, _ = resp.Write(fileData)
	resp.Header().Set(global.HTTP_Content_Type, global.FileTypeMIMEMap[fileInfo.FileType])
	resp.Header().Set(global.HTTP_Content_Disposition, fmt.Sprintf("attachment; filename=%s", fileInfo.FileType))
}

func HandleFuncDeleteResource(resp http.ResponseWriter, req *http.Request) () {
	if req.Method != http.MethodPost {
		resp.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	queryFileUUID, ok := tools.GetFileUUIDFromURL(req)
	if !ok {
		b, _ := json.Marshal(DeleteResponse{StatusCode: ResponseCodeErrorOtherError})
		_, _ = resp.Write(b)
		return
	}

	_, err := tools.GetTokenInfoFromCookies(req)
	if err != nil {
		b, _ := json.Marshal(DeleteResponse{StatusCode: ResponseCodeErrorNotAuth})
		_, _ = resp.Write(b)
		return
	}

	err = action.DeleteFile(queryFileUUID)
	if err != nil {
		b, _ := json.Marshal(DeleteResponse{StatusCode: ResponseCodeErrorInvalidFile})
		_, _ = resp.Write(b)
		return
	}

	b, _ := json.Marshal(DeleteResponse{StatusCode: ResponseCodeSuc})
	_, _ = resp.Write(b)
}

func HandleFuncListResource(resp http.ResponseWriter, req *http.Request) () {
	if req.Method != http.MethodGet {
		resp.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	queryChapterUUID, ok := tools.GetChapterUUIDFromURL(req)
	if !ok {
		b, _ := json.Marshal(DeleteResponse{StatusCode: ResponseCodeErrorInvalidFile})
		_, _ = resp.Write(b)
		return
	}

	_, err := tools.GetTokenInfoFromCookies(req)
	if err != nil {
		b, _ := json.Marshal(ListResponse{StatusCode: ResponseCodeErrorNotAuth})
		_, _ = resp.Write(b)
		return
	}

	listInfo, err := action.ListFile(queryChapterUUID)
	if err != nil {
		b, _ := json.Marshal(ListResponse{StatusCode: ResponseCodeErrorInvalidFile})
		_, _ = resp.Write(b)
		return
	}

	listResult := make([]*FileInfoResponse, 0, 4)
	for _, info := range listInfo {
		r := &FileInfoResponse{
			FileUUID:    info.FileUUID,
			FileType:    global.FileTypeCodeMap[info.FileType],
			FileName:    info.FileName,
			PutTime:     info.PutTime,
			PutUserUUID: info.PutUserUUID,
		}
		listResult = append(listResult, r)
	}

	b, _ := json.Marshal(ListResponse{StatusCode: ResponseCodeSuc, InfoList: listResult})
	_, _ = resp.Write(b)
}

func HandleFuncNotFound(resp http.ResponseWriter, req *http.Request) () {
	_, _ = resp.Write([]byte("404"))
	resp.WriteHeader(http.StatusNotFound)
}
